﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Text;
using System.Net.Sockets;
using System;
using System.Security.Cryptography;
using System.Linq;
using System.Xml.Schema;
using UnityEngine.UI;
using SimpleJSON;

//channel: 1: weixin; 2: zhifubao
//orderstatus: init; success; failed;
[Serializable]
public class Biz {
	public string body;
	public string out_trade_no;
	public string subject;
	public string total_amount;

}

[Serializable]
public class ContentToAli {
	public string app_id;
	public string method;
	public string charset;
	public string sign_type;
	public string sign;
	public string timestamp;
	public string version;
	public string notify_url;
	public string biz_content;
}

public class AliQRPay : MonoBehaviour {

	private readonly string aliPaymentWebUrl = "https://openapi.alipay.com/gateway.do";

	private string validChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	private System.Random random = new System.Random();

	private string privateKey = "MIIEowIBAAKCAQEAwoqm+4Zc9XoosRvmjRmw8R///cIblI2vmIrsXfcdVWN0l5Kkm5cPNrQBAuMDC7OhLj3SRxXdLwj3UiBdxa5GCNWSNMQbJX8PhRkxkqjbA7m0BHfP28hYCUrqtf7n+yZQAs7CFlwWodkQBllOXyKfxdavUV8jCULF5Oh6LMlQdIFBNlR965GbLWidPN+TznUK6dIp2URXIr4b3Kr6sAa+g6XHHy5xWG5DLaDF3RVmxUOXcQVrPZSoXJViTi4tsYUjsI9LCN13LXUBmYWiob53TOc/k0hfg7q/LuJJM07c4EHtvFyXTuX9TQLk8WEx5wizUjsE3PHLiwef1D1qNohdhQIDAQABAoIBAEMkjYtEDXNfzQmLgp/yXQJrs/OF+fTkjdVNJVUtA7/DyqrBnvRgR4zuxWaUqSlxvtBx39BO+H7STmHW7+ZZRqyn4YU6pc/kMZwTXOusQaLghgQAwgmt9MyIQce8iZ5B092UNKJ4+Aq2GvTF/+mPzqr+dJXWCwaYOCrQHdK1TIJYu2P20xB1QJzVFktOE1l4CW9SaoqQwWO9RJtIJ07UtsWz6s+/uDEx6XAnOe/OjL4NbkFmZdBOCa6NYJ0xU0ixIT+KtJxQt9uH2TMEDkidng0ntkEEWz+dPTe7dxdQOt0ULee5Fh623kHV33ow7v3PZoVmwG1OTu16hTgKiOedQyECgYEA82MTfpyaYQTu8/JUQdnaEmEFSwgObhP8ccfkz0t0ZCHbm6QQsDgenxN/emVD6URD4Wv0OKU+1TCrH4O0vVaf27IVB+slmlkuHl7L81sD/uMvfziEamMhPPRJ5YFRvJM6Hb2jG4gX8k2Tmw5cQ1qTZM7UujENgKoQMgTHVgGdBP0CgYEAzJ+QAeLAZBzYBHD24NOkqPoGw1lOIZirUaL/bnaIcWupl099r4zqqq1SyXs7F26d2lF6dpLngmCn6dNcEEEhoQksbVfPMr2fK6G0RXUcP+e5DF5nJciNUjrb8rYDqnpV7c65sGJwDEeyuoK7qzuKwBomssAY5UHx12CpX1JyJSkCgYA+vWWDF9977weh/u/VYdEjN7tjkhoWKjTDJ+JFxY+lWE0CHBLpOi46l44fAQe2lBBjha6ZA4/k6PzTzOHEhRYETi25stYoqaCH16v/eZDYHbxasTkBK7ifu+FotiNZ1yH+FKp9KGHz5ieMqvEBs9GXBPI1ZPu0FSLDohO4igXuCQKBgETOHqlTque+/zA3H/ynjgW31RBY9jm70VepHITzXJvu3N7hPt0J4Fq131Gam1scuf2QSkKW0Tv47l1k0d8xV3KD2CmITka8lkognLKCV5Zxf7rV/48n6cVNv2nUpFTtv/kv+yaBYzXt4f5vOVWxts0zvfF7QKPOUdjfSvSfj3xJAoGBAJ4s9Qz4afMFd7ofac7Z14jZA7li0TcIQoEF9gz57FZGT5n88QKpKtaEgiS1Lva+PRFsMwvKaFTs7foNQONWyfjThPDtWfj1ZewEL+UdZKxbFjTTvflBdwE2R7hY7R4vchcpsmKzG/A2DG8LjUqV775/a5xeAyJ8lI6EnOouoDuN";

	private HttpWebRequest request;
	private RawImage displayImage;

	void Start () {
		//准备
		GameObject.Find("QRCodeEncodeController").GetComponent<QRCodeEncodeController>().onQREncodeFinished += OnQRFinished;
		GameObject.Find ("Canvas/Button").GetComponent<Button> ().onClick.AddListener (QueryOrderStatus);
		displayImage = GameObject.Find ("Canvas/RawImage").GetComponent<RawImage> ();

		//填充字段

		//!!!!!!!!!!!!!!!!!!!!!!!!
		//一定要注意的地方：
		//支付宝的这个文档在这里：https://docs.open.alipay.com/api_1/alipay.trade.precreate
		//1、公共请求参数跟请求参数的区别是：在送给签名的字串里，非公共的是json的字串形式（也就是本身是独立的，作为公共参数里的biz_content）
		//2、送给签名的字串，json字串部分的内容，也得按照字母排列（用unity的序列话方式，class里面的变量名排列好，encode后就是排列好的）
		//3、支付宝给的sdk的C#的代码，即使编译出dll，在PC上可用，在unity也不可用，缺少System.Web
		//4、上述的这份代码，里面有好几个地方的原来的exception被programmer截获，改成了他们自己的出错信息，反而误导人，比如签名错误，可能是默认用keystore的sign key方式，但是电脑没权限，
		//   本来这个exception直接抛出会很好定位，但是被改写成签名错误，此为一；第二个是在mac机上，在动态装载一个cry.dll（mac机上没有这个dll，只有windows有）的时候也会扔出相应的报错，
		//   但是也被programmer改成了误导性的信息。所以，如果报错，还是跟踪一下源码会比较好。
   

		//////////分三个阶段：
		//////////第一阶段：得到签名

		//下面4个是请求参数
		string out_trade_no = DateTime.Now.ToString ("yyyyMMddHHmmssffff") + random.Next (100000, 999999) + GenerateNonce (6) + Guid.NewGuid ().ToString ().Replace("-", ""); //"400400000010101023478768549324";
		string total_amount = "0.01";
		string subject = "购买娃娃（萌物）";
		string body = "10个总共";

		Biz biz = new Biz();
		biz.body = body;
		biz.out_trade_no = out_trade_no;
		biz.subject = subject;
		biz.total_amount = total_amount;

		//主体请求部分，上面的部分会以json格式的字串成为biz_content内容
		string app_id = "2018042460073110";
		string method = "alipay.trade.precreate";
		string charset = "utf-8";
		string sign_type = "RSA2";
		string sign = string.Empty;
		string timestamp = DateTime.Now.ToString ("yyyy-MM-dd HH:mm:ss");
		string version = "1.0";
		string notify_url = "http://games.jcgroup.com.cn/dolltv/alipaynotify.php";
		string biz_content = JsonUtility.ToJson (biz);

		//签名 (RSA2 = sha256withrsa)
		sign = GetSign(app_id, method, charset, sign_type, timestamp, version, biz_content, notify_url, privateKey);

		//////////第二阶段：拼接用来post到阿里的数据
		//发送请求
		ContentToAli cta = new ContentToAli();
		cta.app_id = app_id;
		cta.biz_content = biz_content;
		cta.charset = charset;
		cta.method = method;
		cta.notify_url = notify_url;
		cta.sign = sign;
		cta.sign_type = sign_type;
		cta.timestamp = timestamp;
		cta.version = version;

		string content = BuildContent (cta);
		WebRequest request = PostToUrl (aliPaymentWebUrl, content);

		//////////第三阶段：收到阿里的数据（json格式）
		//得到回应
		string response = GetResponseFromRemote(request);

		//查看返回消息是否是Success
		var N = JSON.Parse(response);
		var publicResponseParameter = N ["alipay_trade_precreate_response"];
		string aliSign = N["sign"];

		string aliCode = publicResponseParameter["code"];
		string aliMsg = publicResponseParameter["msg"];

		if (aliCode.Equals ("10000") || aliMsg.Equals ("Success")) {
			string aliUrl = publicResponseParameter["qr_code"];
			string aliTransactionOrder = publicResponseParameter["out_trade_no"];
			GameObject.Find ("QRCodeEncodeController").GetComponent<QRCodeEncodeController> ().Encode (aliUrl);
		}

	}
	
	string GetSign(string app_id, string method, string charset, string sign_type, 
		string timestamp, string version, string biz_content, string notify_url, string privateKey) {

		string strToSign = string.Empty;

		//第一步，排序;
		//第二步，拼接(同时完成)
		strToSign += "app_id=";
		strToSign += app_id;
		strToSign += "&";

		strToSign += "biz_content=";
		strToSign += biz_content;
		strToSign += "&";

		strToSign += "charset=";
		strToSign += charset;
		strToSign += "&";

		strToSign += "method=";
		strToSign += method;
		strToSign += "&";

		strToSign += "notify_url=";
		strToSign += notify_url;
		strToSign += "&";

		strToSign += "sign_type=";
		strToSign += sign_type;
		strToSign += "&";

		strToSign += "timestamp=";
		strToSign += timestamp;
		strToSign += "&";

		strToSign += "version=";
		strToSign += version;

		//第三步，调用签名方法
		string sign = Helper.RSASign(strToSign, privateKey);
		return sign;
	}

	private string BuildContent(ContentToAli cta) {

		string result = string.Empty;

		result += "biz_content=";
		result += WWW.EscapeURL(cta.biz_content, Encoding.UTF8);
		result += "&";

		result += "method=";
		result += WWW.EscapeURL(cta.method, Encoding.UTF8);
		result += "&";

		result += "version=";
		result += WWW.EscapeURL(cta.version, Encoding.UTF8);
		result += "&";

		result += "app_id=";
		result += WWW.EscapeURL(cta.app_id, Encoding.UTF8);
		result += "&";

		result += "timestamp=";
		result += WWW.EscapeURL(cta.timestamp, Encoding.UTF8);
		result += "&";

		result += "sign_type=";
		result += WWW.EscapeURL(cta.sign_type, Encoding.UTF8);
		result += "&";

		result += "charset=";
		result += WWW.EscapeURL(cta.charset, Encoding.UTF8);
		result += "&";

		result += "notify_url=";
		result += WWW.EscapeURL(cta.notify_url, Encoding.UTF8);
		result += "&";

		result += "sign=";
		result += WWW.EscapeURL(cta.sign, Encoding.UTF8);

		return result;

	}

	private string GenerateNonce(int length) {
		var nonceString = new StringBuilder ();
		for (int i = 0; i < length; ++i) {
			int index = random.Next (0, validChars.Length - 1);
			nonceString.Append (validChars [index]);
		}
		return nonceString.ToString ();
	}

	//event from qr controller
	private void OnQRFinished(Texture2D image) {
		displayImage.texture = image;
	}

	private void QueryOrderStatus() {
		
	}

	private WebRequest PostToUrl(string url, string content) {
		byte[] data = Encoding.UTF8.GetBytes(content);
		request = WebRequest.Create (url) as HttpWebRequest;
		request.Method = "POST";
		request.Timeout = 10000;
		request.ContentType = "application/x-www-form-urlencoded;charset=utf-8";
		request.ContentLength = data.Length;

		try {
			using (Stream stream = request.GetRequestStream())
			{
				stream.Write(data, 0, data.Length);
			}
		}
		catch (Exception e) {
			Debug.LogError (e);
			return null;
		}

		return request;
	}

	private string GetResponseFromRemote(WebRequest request) {
		if (request == null)
			return null;

		string remoteResp = string.Empty;
		try {
			WebResponse response;
			Stream respStream;
			byte[] buffer;

			response = request.GetResponse();
			int bufferLength = (int) response.ContentLength;

			if (bufferLength <= 0) {
				Debug.LogError("");
				return null;
			}

			long totalSize = bufferLength;
			buffer = new byte[totalSize];
			respStream = null;
			long readPointer = 0;
			int offset = 0;
			int receivedBytesCount = 0;
			respStream = response.GetResponseStream();
			do
			{
				receivedBytesCount = respStream.Read(buffer, offset, bufferLength - offset);
				offset += receivedBytesCount;
				if (receivedBytesCount > 0)
				{
					readPointer += receivedBytesCount;
				}
			}
			while (receivedBytesCount != 0);

			remoteResp = Encoding.UTF8.GetString(buffer);
		}
		catch (Exception e) {
			Debug.LogError (e.ToString());
		}

		return remoteResp;
	}
}
